 /*******************************************************************************
  * Copyright (c) 2000, 2005 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.ide.registry;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.Iterator ;

 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IProjectNatureDescriptor;
 import org.eclipse.core.resources.IWorkspace;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.ui.internal.ide.Category;
 import org.eclipse.ui.model.IWorkbenchAdapter;
 import org.eclipse.ui.model.WorkbenchAdapter;

 /**
  * This class represents a registry of project capabilities and categories of
  * capabilities.
  */
 public class CapabilityRegistry extends WorkbenchAdapter implements IAdaptable {
     private static final String [] EMPTY_ID_LIST = new String [0];

     private static final Capability[] EMPTY_CAP_LIST = new Capability[0];

     private HashMap natureToCapability;

     private ArrayList capabilities;

     private ArrayList categories;

     private Category miscCategory;

     /**
      * Creates a new instance of <code>CapabilityRegistry</code>
      */
     public CapabilityRegistry() {
         capabilities = new ArrayList (30);
         categories = new ArrayList (15);
     }

     /**
      * Adds the given capability to the registry. Called by
      * the CapabilityRegistryReader.
      */
     /* package */boolean addCapability(Capability capability) {
         return capabilities.add(capability);
     }

     /**
      * Adds the given capability category to the registry. Called
      * by the CapabilityRegistryReader.
      */
     /* package */boolean addCategory(Category category) {
         return categories.add(category);
     }

     /**
      * Finds the capability for the given identifier, or
      * <code>null</code> if none.
      */
     public Capability findCapability(String id) {
         Iterator itr = capabilities.iterator();
         while (itr.hasNext()) {
             Capability cap = (Capability) itr.next();
             if (id.equals(cap.getId())) {
                 return cap;
             }
         }
         return null;
     }

     /**
      * Finds the category for the given identifier, or
      * <code>null</code> if none.
      */
     public Category findCategory(String id) {
         Iterator itr = categories.iterator();
         while (itr.hasNext()) {
             Category cat = (Category) itr.next();
             if (id.equals(cat.getRootPath())) {
                 return cat;
             }
         }
         return null;
     }

     /**
      * Finds the capability for each specified identifier.
      * Any <code>null</code> entries in the resulting array
      * are for identifiers to which no capability exist.
      */
     public Capability[] findCapabilities(String [] ids) {
         int count = capabilities.size();
         Capability[] results = new Capability[ids.length];

         for (int i = 0; i < ids.length; i++) {
             String id = ids[i];
             for (int j = 0; j < count; j++) {
                 Capability cap = (Capability) capabilities.get(j);
                 if (cap.getId().equals(id)) {
                     results[i] = cap;
                     break;
                 }
             }
         }

         return results;
     }

     /**
      * Finds the category for each specified identifier.
      * Any <code>null</code> entries in the resulting array
      * are for identifiers to which no category exist.
      *
      * @return an array of <code>ICategory</code>
      */
     public Category[] findCategories(String [] ids) {
         int count = categories.size();
         Category[] results = new Category[ids.length];

         for (int i = 0; i < ids.length; i++) {
             String id = ids[i];
             for (int j = 0; j < count; j++) {
                 Category cat = (Category) categories.get(j);
                 if (cat.getId().equals(id)) {
                     results[i] = cat;
                     break;
                 }
             }
         }

         return results;
     }

     /* (non-Javadoc)
      * Method declared on IAdaptable.
      */
     public Object getAdapter(Class adapter) {
         if (adapter == IWorkbenchAdapter.class)
             return this;
         else
             return null;
     }

     /**
      * Returns the list of categories in the registry
      * which contain at least one capability. Does not
      * include the misc and unknown categories.
      */
     public ArrayList getUsedCategories() {
         ArrayList results = new ArrayList (categories.size());
         Iterator itr = categories.iterator();
         while (itr.hasNext()) {
             Category cat = (Category) itr.next();
             if (cat.hasElements())
                 results.add(cat);
         }
         return results;
     }

     /**
      * Returns the capability for the nature id
      */
     public Capability getCapabilityForNature(String natureId) {
         return (Capability) natureToCapability.get(natureId);
     }

     /**
      * Returns the list of capabilities in the registry
      */
     public ArrayList getCapabilities() {
         return capabilities;
     }

     /* (non-Javadoc)
      * Method declared on IWorkbenchAdapter.
      */
     public Object [] getChildren(Object o) {
         return capabilities.toArray();
     }

     /**
      * Returns the membership set ids that the specified
      * capability belongs to.
      */
     public String [] getMembershipSetIds(Capability capability) {
         IProjectNatureDescriptor desc = capability.getNatureDescriptor();
         if (desc == null)
             return EMPTY_ID_LIST;

         return desc.getNatureSetIds();
     }

     /**
      * Returns the miscellaneous category, or <code>null</code>
      * if none.
      */
     public Category getMiscCategory() {
         return miscCategory;
     }

     /**
      * Returns the capability ids that are prerequisites
      * of the specified capability.
      */
     public String [] getPrerequisiteIds(Capability capability) {
         IProjectNatureDescriptor desc = capability.getNatureDescriptor();
         if (desc == null)
             return EMPTY_ID_LIST;

         String [] natureIds = desc.getRequiredNatureIds();
         if (natureIds.length == 0)
             return EMPTY_ID_LIST;

         ArrayList results = new ArrayList (natureIds.length);
         for (int i = 0; i < natureIds.length; i++) {
             Capability cap = (Capability) natureToCapability.get(natureIds[i]);
             if (cap != null)
                 results.add(cap.getId());
         }

         if (results.size() == 0) {
             return EMPTY_ID_LIST;
         } else {
             String [] ids = new String [results.size()];
             results.toArray(ids);
             return ids;
         }
     }

     /**
      * Returns the capabilities assigned to the specified project
      */
     public Capability[] getProjectCapabilities(IProject project) {
         try {
             String [] natureIds = project.getDescription().getNatureIds();
             ArrayList results = new ArrayList (natureIds.length);
             for (int i = 0; i < natureIds.length; i++) {
                 Capability cap = (Capability) natureToCapability
                         .get(natureIds[i]);
                 if (cap == null) {
                     cap = new Capability(natureIds[i]);
                     mapCapability(cap);
                 }
                 results.add(cap);
             }

             if (results.size() == 0) {
                 return EMPTY_CAP_LIST;
             } else {
                 Capability[] caps = new Capability[results.size()];
                 results.toArray(caps);
                 return caps;
             }
         } catch (CoreException e) {
             return EMPTY_CAP_LIST;
         }
     }

     /**
      * Returns the capabilities assigned to the specified project
      * that are consideed disabled by core.
      */
     public Capability[] getProjectDisabledCapabilities(IProject project) {
         try {
             String [] natureIds = project.getDescription().getNatureIds();
             ArrayList results = new ArrayList (natureIds.length);
             for (int i = 0; i < natureIds.length; i++) {
                 if (!project.isNatureEnabled(natureIds[i])) {
                     Capability cap = (Capability) natureToCapability
                             .get(natureIds[i]);
                     if (cap == null) {
                         cap = new Capability(natureIds[i]);
                         mapCapability(cap);
                     }
                     results.add(cap);
                 }
             }

             if (results.size() == 0) {
                 return EMPTY_CAP_LIST;
             } else {
                 Capability[] caps = new Capability[results.size()];
                 results.toArray(caps);
                 return caps;
             }
         } catch (CoreException e) {
             return EMPTY_CAP_LIST;
         }
     }

     /**
      * Returns whether the registry contains any capabilities.
      */
     public boolean hasCapabilities() {
         return !capabilities.isEmpty();
     }

     /**
      * Returns whether the specified capability has any prerequisites.
      */
     public boolean hasPrerequisites(Capability capability) {
         return getPrerequisiteIds(capability).length > 0;
     }

     /**
      * Loads capabilities and capability categories from the platform's plugin
      * registry.
      */
     public void load() {
         CapabilityRegistryReader reader = new CapabilityRegistryReader();
         reader.read(Platform.getExtensionRegistry(), this);
         mapCapabilities();
     }

     /**
      * Maps each capability in the registry to a particular category.
      * The category is defined in xml. If the capability's category is
      * not found, then the capability is added to the "misc" category.
      * <p>
      * Maps each capability in the registry to a particular nature
      * id.
      */
     /* package */void mapCapabilities() {
         natureToCapability = new HashMap ();

         Iterator itr = capabilities.iterator();
         while (itr.hasNext())
             mapCapability((Capability) itr.next());
     }

     private void mapCapability(Capability cap) {
         // Map to category
 if (!cap.isValid()) {
             if (miscCategory == null)
                 miscCategory = new Category();
             miscCategory.addElement(cap);
         } else {
             Category cat = null;
             String catPath = cap.getCategoryPath();
             if (catPath != null)
                 cat = findCategory(catPath);
             if (cat != null) {
                 cat.addElement(cap);
             } else {
                 if (miscCategory == null)
                     miscCategory = new Category();
                 miscCategory.addElement(cap);
             }
         }

         // Map to nature id
 natureToCapability.put(cap.getNatureId(), cap);
     }

     /**
      * Removes from the capability collection all capabilities
      * whose UI is handle by another capability in the collection.
      * The provided collection must be in proper prerequisite order.
      *
      * @param capabilities the capabilities to be pruned
      * @return a collection of capabilities pruned
      */
     public Capability[] pruneCapabilities(Capability[] capabilities) {
         ArrayList ids = new ArrayList (capabilities.length);
         for (int i = 0; i < capabilities.length; i++)
             ids.add(capabilities[i].getId());

         for (int i = 0; i < capabilities.length; i++) {
             ArrayList handleIds = capabilities[i].getHandleUIs();
             if (handleIds != null)
                 ids.removeAll(handleIds);
         }

         String [] results = new String [ids.size()];
         ids.toArray(results);
         return findCapabilities(results);
     }

     /**
      * Checks that the collection is valid. If so, the collection is
      * ordered based on prerequisite.
      *
      * @param capabilities the capabilities to be checked and ordered
      * @return a status object with code <code>IStatus.OK</code> if
      * the given set of natures is valid, otherwise a status
      * object indicating what is wrong with the set. Also, the
      * collection of capabilities specified is ordered based on
      * prerequisite.
      */
     public IStatus validateCapabilities(Capability[] capabilities) {
         String natures[] = new String [capabilities.length];
         for (int i = 0; i < capabilities.length; i++)
             natures[i] = capabilities[i].getNatureId();

         IWorkspace workspace = ResourcesPlugin.getWorkspace();
         IStatus status = workspace.validateNatureSet(natures);
         if (status.isOK()) {
             natures = workspace.sortNatureSet(natures);
             for (int i = 0; i < natures.length; i++)
                 capabilities[i] = (Capability) natureToCapability
                         .get(natures[i]);
         }

         return status;
     }
 }

